using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace Common.Shared.Windows.Forms {
  public class MonitorMeter : Control {
    private int barValue = 25;
    private int barCount = 0;
    private Rectangle rectValue;
    private Rectangle rectBarContainer;
    private Rectangle rectBar;
    private int barHeight = 4;
    private int barPadding = 8;
    private bool showTitle;
    private Rectangle rectTitle = Rectangle.Empty;

    /// <summary>
    /// Initializes a new instance of the <see cref="MonitorMeter"/> class.
    /// </summary>
    public MonitorMeter ( ) {
      SetStyle ( ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true );
    }


    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.Resize"></see> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
    protected override void OnResize ( EventArgs e ) {
      base.OnResize ( e );
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.Paint"></see> event.
    /// </summary>
    /// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"></see> that contains the event data.</param>
    protected override void OnPaint ( PaintEventArgs e ) {
      Graphics g = e.Graphics;
      g.Clear ( this.BackColor );

      if ( this.BackgroundImage != null )
        g.DrawImage ( this.BackgroundImage, this.ClientRectangle );

      StringFormat sf = new StringFormat ( );
      sf.Alignment = StringAlignment.Center;
      sf.LineAlignment = StringAlignment.Near;

      SizeF sizeF = g.MeasureString ( "0", this.Font, this.rectValue.Width, sf );
      if ( showTitle )
        this.rectTitle = new Rectangle ( 0, 0, this.ClientRectangle.Width, (int)sizeF.Height + 2 );
      else
        this.rectTitle = Rectangle.Empty;

      this.rectBarContainer = new Rectangle ( this.barPadding, rectTitle == Rectangle.Empty ? 4 : rectTitle.Bottom + 4, this.Width - ( this.barPadding * 2 ), this.Height - rectTitle.Bottom - ( (int)sizeF.Height + 10 ) );
      rectBar = new Rectangle ( barPadding, rectBarContainer.Bottom - 10, rectBarContainer.Width, barHeight );
      barCount = ( rectBarContainer.Height / ( rectBar.Height + 4 ) );
      this.rectValue = new Rectangle ( 0, rectBarContainer.Bottom + 2, this.Width, this.Height - rectBarContainer.Bottom - 2 );

      if ( this.showTitle )
        drawTitle ( g );
      drawBars ( g );
      drawValue ( g );
    }

    /// <summary>
    /// Draws the title.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawTitle ( Graphics g ) {
      StringFormat sf = new StringFormat ( );
      sf.Alignment = StringAlignment.Center;
      sf.LineAlignment = StringAlignment.Near;
      g.DrawString ( this.Text, this.Font, new SolidBrush ( this.ForeColor ), this.rectTitle, sf );
    }

    /// <summary>
    /// Draws the value.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawValue ( Graphics g ) {
      StringFormat sf = new StringFormat ( );
      sf.Alignment = StringAlignment.Center;
      sf.LineAlignment = StringAlignment.Near;
      g.DrawString ( string.Format ( "{0}%", this.barValue ), this.Font, new SolidBrush ( this.ForeColor ), this.rectValue, sf );
    }

    /// <summary>
    /// Draws the bars.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawBars ( Graphics g ) {
      barCount = ( rectBarContainer.Height / ( rectBar.Height + 4 ) );
      int bCount = 0;
      while ( rectBar.Top > rectBarContainer.Top ) {
        if ( Percent ( this.barValue, barCount ) > bCount )
          drawActiveBar ( g );
        else
          drawInactiveBar ( g );
        int x = ( ( rectBar.Width + ( barPadding * 2 ) ) / 2 );
        rectBar.Offset ( 0, -( rectBar.Height + 4 ) );
        bCount++;
      }
      Rectangle rect = new Rectangle ( ( ( rectBar.Width / 2 ) - 3 ) + this.rectBar.Left, rectBarContainer.Top, 6, this.rectBarContainer.Height );

      //if ( this.BackgroundImage == null ) {
        g.FillRectangle ( new SolidBrush ( this.BackColor ), rect );
      //} else {
      //  g.FillRectangle ( new SolidBrush ( this.BackColor ), rect );
      //  g.DrawImage ( this.BackgroundImage, rect, rect, GraphicsUnit.Pixel );
      //}
      rectBar = new Rectangle ( barPadding, rectBarContainer.Bottom - 10, this.Width - ( barPadding * 2 ), barHeight );
    }

    /// <summary>
    /// Percents the specified a.
    /// </summary>
    /// <param name="a">A.</param>
    /// <param name="b">The b.</param>
    /// <returns></returns>
    private int Percent ( int a, int b ) {
      try {
        if ( b == 0 || a == 0 )
          return 0;
        double x = (double)a / 100d;
        double ret = ( x * b );
        return ret > 0 ? ret < 1 ? 1 : (int)ret : (int)ret;
      } catch { return a > 0 ? 1 : 0; }
    }

    /// <summary>
    /// Draws the active bar.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawActiveBar ( Graphics g ) {
      g.FillRectangle ( new SolidBrush ( this.ForeColor ), rectBar );
    }

    /// <summary>
    /// Draws the inactive bar.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawInactiveBar ( Graphics g ) {
      Color clr = Color.FromArgb ( 100, this.ForeColor );
      g.FillRectangle ( new SolidBrush ( clr ), rectBar );
    }

    /// <summary>
    /// Gets the bar count.
    /// </summary>
    /// <value>The bar count.</value>
    public int BarCount { get { return this.barCount; } }
    /// <summary>
    /// Gets or sets the height of the bar.
    /// </summary>
    /// <value>The height of the bar.</value>
    public int BarHeight { get { return this.barHeight; } set { this.barHeight = value < 2 ? 2 : value; this.Invalidate ( ); } }
    /// <summary>
    /// Gets or sets the value.
    /// </summary>
    /// <value>The value.</value>
    public int Value { get { return this.barValue; } set { this.barValue = value > 100 ? 100 : value < 0 ? 0 : value; this.Invalidate ( ); } }
    /// <summary>
    /// Gets or sets the bar padding.
    /// </summary>
    /// <value>The bar padding.</value>
    public int BarPadding { get { return this.barPadding; } set { this.barPadding = value > 2 ? value : 2; this.Invalidate ( ); } }
    /// <summary>
    /// Gets or sets a value indicating whether [show title].
    /// </summary>
    /// <value><c>true</c> if [show title]; otherwise, <c>false</c>.</value>
    public bool ShowTitle {
      get { return this.showTitle; }
      set {
        if ( this.showTitle != value ) {
          this.showTitle = value;
          this.Invalidate ( );
        }
      }
    }
  }

}
